package gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToggleButton;
import javax.swing.Timer;
import klotski.KlotskiBoard;
import klotski.KlotskiBoard.KMove;
import klotski.games.Block10ProBoard;
import klotski.games.BlockProBoard;
import klotski.games.Climb12Board;
import klotski.games.Climb12ProBoard;
import klotski.games.RedDonkeyBoard;

import model.Board;
import model.BreadthFirstSearch;
import model.DepthFirstSearch;
import model.Search;

class MainPanel {

    KlotskiBoard board;
    JPanel content;
    BoardPanel panel;
    Search search;
    boolean display = false;
    int delayMillis = 1;
    private final Observer displayer;
    boolean solved = false;
    JToggleButton disp;
    JButton showMoves;
    private final JLabel status;
    private final JButton depth;
    private final JButton breadth;
    Timer tt;

    void assertDisplay() {
        if (disp.isSelected()) {
            board.addObserver(displayer);
        } else {
            board.deleteObserver(displayer);
        }
    }

    MainPanel() {


        panel = new BoardPanel();

        status = new JLabel(" Select a game ");

        JPanel buttons = new JPanel();
        buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS));

        buttons.add(init());

        buttons.add(Box.createVerticalGlue());


        disp = new JToggleButton("Display");
        disp.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                assertDisplay();
            }
        });

        buttons.add(disp);

        displayer = new Observer() {

            public void update(Observable o, Object arg) {
                panel.repaint();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        };



        content = new JPanel(new BorderLayout());
        content.add(buttons, BorderLayout.EAST);
        content.add(status, BorderLayout.SOUTH);


        depth = new JButton("Depth First Search");

        buttons.add(depth);

        depth.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                board.reset();
                search = new DepthFirstSearch(board);
                search();
            }
        });


        breadth = new JButton("Breadth First Search");

        buttons.add(breadth);
        breadth.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                board.reset();
                search = new BreadthFirstSearch(board);
                search();
            }
        });





        showMoves = new JButton("Show Solution");
        showMoves.setEnabled(false);

        depth.setEnabled(false);
        breadth.setEnabled(false);
        buttons.add(showMoves);
        showMoves.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                Thread t = new Thread(new Runnable() {

                    public void run() {
                        showMoves();
                    }
                });
                t.start();
            }
        });

        //   filler.setPreferredSize(new Dimension(200,800));
        buttons.add(Box.createVerticalGlue());
        content.add(panel, BorderLayout.CENTER);


        setBoard(new RedDonkeyBoard());
    }

    void statusUpdateInit() {

        tt = new Timer(20, new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                if (solved) {
                    tt.stop();
                    tt = null;

                } else {
                    status.setText(" Searching:   Tree Depth =" + ((Board) board).getDepth() + " Nodes evaluated =" + board.getWork());
                    //        panel.repaint();
                }
            }
        });

        tt.start();
    }

//    void repaint() {
//        if (panel == null) {
//            return;
//        }
//        panel.repaint();
//        // board.asciiDump();
//        try {
//            Thread.sleep(delayMillis);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//    }
    void showMoves() {
        if (!solved) {
            status.setText(" Please solve the puzzle");
            return;

        }



        Vector<KMove> moves = new Vector<KMove>(board.history);

        while (board.undo()) {
        }    // rewind

        panel.repaint();
        delayMillis =
                200;
        for (KMove move : moves) {
            try {
                Thread.sleep(delayMillis);
            } catch (InterruptedException ex) {
                Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex);
            }
            move.doMove();

            panel.repaint();
        }

    }

    void search() {
        solved = false;
        statusUpdateInit();
        showMoves.setEnabled(false);
        assertDisplay();
//        if (disp.isSelected()) {
//            board.addObserver(displayer);
//        } else {
//            board.deleteObserver(displayer);
//        }

        Thread t = new Thread(new Runnable() {

            public void run() {
                switch (search.explore()) {
                    case Search.WIN:
                        status.setText(" WIN!!  Tree Depth =" + ((Board) board).getDepth() + " Nodes evaluated =" + board.getWork());
                        panel.repaint();
                        solved = true;
                        showMoves.setEnabled(true);
                        break;

                    case Search.FAIL:
                        status.setText(" FAILED ");
                        break;

                }


            }
        });
        t.start();
    }

    abstract class MyAction {

        private final String name;

        MyAction(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }

        abstract void fire();
    };

    JComboBox init() {
        // JMenuBar menuBar = new JMenuBar();


        final JComboBox combo = new JComboBox();
        // combo.setPreferredSize(new Dimension(100,20));
        combo.setMaximumSize(new Dimension(500, 20));


        combo.addItem(new MyAction("RedDonkey") {

            public void fire() {
                setBoard(new RedDonkeyBoard());

            }
        });

        combo.addItem(new MyAction("Pro") {

            public void fire() {
                setBoard(new BlockProBoard());

            }
        });

        combo.addItem(new MyAction("10Pro") {

            public void fire() {
                setBoard(new Block10ProBoard());
            }
        });


        combo.addItem(new MyAction("Climb12") {

            public void fire() {
                setBoard(new Climb12Board());

            }
        });



        combo.addItem(new MyAction("Climb12Pro") {

            public void fire() {
                setBoard(new Climb12ProBoard());
            }
        });

        combo.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                MyAction act = (MyAction) combo.getSelectedItem();
                act.fire();
            }
        });
        return combo;

    }

    private void setBoard(KlotskiBoard board) {
        showMoves.setEnabled(false);
        solved = false;
        status.setText("Ready to search ");
        depth.setEnabled(true);
        breadth.setEnabled(true);
        this.board = (KlotskiBoard) board;
        panel.setBoard(board);
        content.invalidate();
        panel.repaint();
    }
}
